// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification // for details on configuring this project to bundle and minify static web assets. function clamp(min, value, max) { return Math.max(Math.min(value, max), min); } // Initialize OverlayScrollbars document.addEventListener("DOMContentLoaded", () => { let bodyScroller = window.OverlayScrollbars(document.body, { callbacks: { onScroll: function (ev) { const scrollInfo = bodyScroller.scroll(); const scrollTop = ev.target.scrollTop; const max = scrollInfo.max.y; floatingNavBar(max, scrollTop); } } }); }); // drop-in $.ajax replacement with identical lifecycle and cancellation behavior // please don't use this for new code function notAjax(options) { const { url, type: method, data, beforeSend, success, error, complete } = options; const params = data && Object.fromEntries([...Object.entries(data)].filter(([k, v]) => v != null)); const queryString = params && "?" + new URLSearchParams(params) || ""; const abortController = new AbortController(); (async () => { beforeSend && beforeSend(); try { const response = await fetch(url + queryString, { method, signal: abortController.signal // `credentials: "include"` is intentionally omitted - without, cookies will only be sent to same origin }); if (!response.ok) { throw new Error((await response.text()) || "Kaputt!"); } success && success(await response.text()); } catch (ex) { error && error(ex); } finally { complete && complete(); } })(); return abortController; } // Dropdowns if (document.querySelector('.dropdown-btn')) { const btns = document.querySelectorAll('.dropdown-btn'); const menus = document.querySelectorAll('.dropdown-menu'); btns.forEach(btn => { btn.addEventListener('mousedown', e => { e.preventDefault(); e.stopPropagation(); if (e.target.classList.contains('btn')) e.target.focus(); }); btn.addEventListener('click', e => { if (e.target.classList.contains('select-input')) e.target.focus(); const openMenus = document.querySelectorAll('.dropdown-menu.open'); const menu = e.target.parentElement.querySelector('.dropdown-menu'); e.stopPropagation(); if (!menu.classList.contains('open')) { openMenus.forEach(openMenu => { openMenu.classList.remove('open'); }); } btn.childNodes.forEach(node => { node.addEventListener('click', e => e.stopPropagation()); }); if (menu.classList.contains('open')) { menu.classList.add('closing'); setTimeout(() => { menu.classList.remove('closing', 'open'); }, 150); } else { menu.classList.add('open'); } }); }); window.addEventListener('keydown', e => { if (e.key === 'Escape') { e.stopPropagation(); menus.forEach(menu => { if (menu.classList.contains('open')) { menu.classList.add('closing'); setTimeout(() => { menu.classList.remove('closing', 'open'); }, 150); } }) } }) menus.forEach(menu => { menu.addEventListener('click', e => e.stopPropagation()); menu.childNodes.forEach(node => { node.addEventListener('click', e => e.stopPropagation()); }); menu.querySelectorAll('.menu-item').forEach(item => { item.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') item.click(); }); }); }); document.body.addEventListener("click", e => { document.querySelectorAll('.dropdown-menu').forEach(menu => { if (menu.classList.contains('open')) { menu.classList.add('closing'); setTimeout(() => { menu.classList.remove('closing', 'open'); }, 150); } }); }); } // Modals const modals = document.querySelectorAll('.modal-container'); function toggleModal(node) { if (node) { if (node.classList.contains('open')) { node.classList.add('closing'); setTimeout(() => { node.classList.remove('closing', 'open'); }, 150); } else { node.classList.add('open'); node.focus(); } } } function disableModalButton(e) { e.target.setAttribute('disabled', ''); e.target.innerHTML = `
`; } window.addEventListener('keydown', e => { if (e.key === 'Escape') { modals.forEach(modal => { if (modal.classList.contains('open')) { modal.classList.add('closing'); setTimeout(() => { modal.classList.remove('closing', 'open'); }, 150); } }); } }); // Checkboxes const checkboxes = document.querySelectorAll('.checkbox'); checkboxes.forEach(item => { item.addEventListener('keypress', e => { if (e.key === 'Enter' || e.key === ' ') { e.target.click(); } }); }); // Searchbars const searchbars = document.querySelectorAll('.search-container'); if (searchbars) { searchbars.forEach(searchbar => { const searchInput = searchbar.querySelector('input'); const clearBtn = searchbar.querySelector('.search-clear-btn'); searchInput.addEventListener('input', e => { if (e.target.value) { clearBtn.classList.add('visible'); } else { clearBtn.classList.remove('visible'); } }); clearBtn.addEventListener('click', e => { searchInput.value = ''; clearBtn.classList.remove('visible'); }); }); } // Quick downloader window.addEventListener('keydown', e => { if (!localStorage.getItem('quickSelectMode')) localStorage.setItem('quickSelectMode', 'download'); if ((e.ctrlKey && e.key === 'k') || e.ctrlKey && e.key === '/') { e.preventDefault(); const modalContainerDOM = document.getElementById('quickselect-modal'); if (modalContainerDOM) { toggleModal(modalContainerDOM); if (modalContainerDOM.classList.contains('open')) modalContainerDOM.querySelector('.text-input').focus(); return; } else { const viewIcon = ``; const downloadIcon = ``; const modalContainer = Object.assign(document.createElement('div'), { className: 'modal-container open', id: 'quickselect-modal', innerHTML : ` ` }); tippy.delegate('body', { target: '.quickselect-mode-toggle', hideOnClick: false, offset: 0, placement: 'left', content: `Current Mode: ${localStorage.getItem('quickSelectMode')[0].toUpperCase() + localStorage.getItem('quickSelectMode').slice(1)}`, theme: 'default' }); modalContainer.querySelector('.quickselect-mode-toggle').addEventListener('click', e => { const currentMode = localStorage.getItem('quickSelectMode'); if (currentMode === 'download') { localStorage.setItem('quickSelectMode', 'view'); e.target.innerHTML = viewIcon; e.target._tippy.setContent(`Current Mode: ${localStorage.getItem('quickSelectMode')[0].toUpperCase() + localStorage.getItem('quickSelectMode').slice(1)}`); } else if (currentMode === 'view') { localStorage.setItem('quickSelectMode', 'download'); e.target.innerHTML = downloadIcon; e.target._tippy.setContent(`Current Mode: ${localStorage.getItem('quickSelectMode')[0].toUpperCase() + localStorage.getItem('quickSelectMode').slice(1)}`); } }); let selectedIndex = 0; let setIndex = index => { const visibleItems = modalContainer.querySelectorAll('li:not(.hidden)'); if (visibleItems.length) { modalContainer.querySelectorAll('li.selected').forEach(item => { item.classList.remove('selected'); }); selectedIndex = index; visibleItems[selectedIndex].classList.add('selected'); visibleItems[selectedIndex].scrollIntoView({block: 'nearest'}); } } async function getItems() { await fetch(`https://api.${window.location.host}/latest/store/addons`) .then(result => result.json()) .then(result => { result.forEach(addon => { const item = Object.assign(document.createElement('li'), { innerHTML: `${(addon.type === 'theme' ? ` ` : ` `)} ${addon.name}` }); item.setAttribute('data-author', addon.author); item.setAttribute('data-tags', `[${addon.tags.toString().replace(/,/g, ', ')}]`); item.addEventListener('click', e => { if (localStorage.getItem('quickSelectMode') === 'download') { window.location.href = `${window.location.origin}/download?id=${addon.id}`; } else if (localStorage.getItem('quickSelectMode') === 'view') { window.location.href = `${window.location.origin}/${addon.type}?id=${addon.id}`; } item.addEventListener('keypress', e => { if (e.key === 'Enter' || e.key === ' ') { e.target.click(); } }); }); modalContainer.querySelector('.modal-content').appendChild(item); }); setIndex(0); }); } getItems(); const searchInput = modalContainer.querySelector('.text-input'); let handleKeyboard = e => { const visibleItems = modalContainer.querySelectorAll('li:not(.hidden)'); if (e.key === 'ArrowDown' && !(selectedIndex >= visibleItems.length - 1)) { setIndex(selectedIndex + 1); } else if (e.key === 'ArrowDown' && selectedIndex >= visibleItems.length - 1) { setIndex(0); } else if (e.key === 'ArrowUp' && !(selectedIndex === 0)) { setIndex(selectedIndex - 1); } else if (e.key === 'ArrowUp' && selectedIndex === 0) { setIndex(visibleItems.length - 1); } if (e.key === 'Escape') { toggleModal(document.getElementById('quickselect-modal')); } } document.body.appendChild(modalContainer); searchInput.focus(); searchInput.addEventListener('input', e => { e.preventDefault(); const symbols = ['@', '#']; modalContainer.querySelectorAll('li').forEach(item => { if (!symbols.includes(e.target.value.charAt(0))) { if (!item.innerText.toLowerCase().includes(e.target.value.toLowerCase())) { item.classList.add('hidden'); } else if (item.classList.contains('hidden')) { item.classList.remove('hidden'); } } else if (e.target.value.startsWith('@')) { if (!item.getAttribute('data-author').toLowerCase().includes(e.target.value.substring(1).toLowerCase())) { item.classList.add('hidden'); } else if (item.classList.contains('hidden')) { item.classList.remove('hidden'); } } else if (e.target.value.startsWith('#')) { /*let dataTags = item.getAttribute('data-tags').replaceAll(" ", "").split(','); if (!dataTags.every(v => e.target.value.substring(1).toLowerCase().replaceAll(" ", "").split(',').includes(v))) { item.classList.add('hidden'); } else if (item.classList.contains('hidden')) { item.classList.remove('hidden'); }*/ if (!item.getAttribute('data-tags').includes(e.target.value.substring(1).toLowerCase())) { item.classList.add('hidden'); } else if (item.classList.contains('hidden')) { item.classList.remove('hidden'); } } if (symbols.includes(e.target.value)) { item.classList.remove('hidden'); } }); setIndex(0); }); searchInput.addEventListener('keydown', e => { if (e.key === 'Enter' && modalContainer.querySelectorAll('li.selected:not(.hidden)').length) { modalContainer.querySelectorAll('li.selected:not(.hidden)')[0].click(); } }); setIndex(0); modalContainer.querySelector('.modal-backdrop').addEventListener('click', e => { toggleModal(document.getElementById('quickselect-modal')); }); modalContainer.addEventListener('keydown', handleKeyboard); } } }); // DOM Rendering const parseHTML = function (string) { const template = document.createElement("div"); template.innerHTML = string; template.style = "display: contents;"; return template; } const renderChildren = function (element, children) { children = children.flat(10); for (let i = 0; i < children.length; i++) { const child = children[i]; if (!child) continue; if (typeof child === "string" && child.charAt(0) === "<") element.appendChild(parseHTML(child)); else element.append(child); } }; const createElement = function (type, props = {}, ...children) { props = props || {}; let element; if (typeof type === "function") { element = type(Object.assign(props, {children: props.children || children})); if (!(element instanceof Element) && element !== null) throw "Components must return an HTMLElement"; } else if (typeof type === "string") { element = document.createElement(type); if (children.length) renderChildren(element, children); for (let prop in props) { if (prop === "children") { renderChildren(element, Array.isArray(props.children) ? props.children : [props.children]); } else if (prop.indexOf("on") === 0) { element[prop.toLowerCase()] = props[prop]; } else if (prop === "className") { element.classList.add(...props[prop].split(" ")); } else if (prop in element) { element[prop] = props[prop]; } else { element.setAttribute(prop, props[prop]); } } } if (!element && element !== null) throw "Element could not be rendered!"; return element; }; const h = createElement; async function copyInnerText(target) { var msg, toast; try { await navigator.clipboard.writeText((target.querySelector(".copy-me") || target).innerText); msg = "Copied to clipboard"; } catch (err) { msg = err.message; } target.appendChild(toast = Object.assign(document.createElement("div"), { innerText: msg, className: "copy-toast", })); setTimeout(() => toast.remove(), 1000); }